# -*- coding: utf-8 -*- #

import json
import conf.settings as settings
from os import getpid
from multiprocessing import Queue
from clogger import logger
from fastapi import FastAPI, HTTPException, Response
from prometheus_client import Gauge, CollectorRegistry, generate_latest
from conf.settings import YAML_CONFIG
from sysom_utils import CmgPlugin, SysomFramework
from lib.score_result import ScoreType
from lib.metric_manager import MetricManager
from lib.algorithm.weight_algorithm import WeightsCalculator
from app.routers import health
from app.calculator.analyzer import Analyzer
from app.calculator.diagnose_worker import DiagnoseWorker

app = FastAPI()

app.include_router(health.router, prefix="/api/v1/cluster_health/health")
# app.include_router(health.router, prefix="/api/v1/cluster_health/person")

CLUSTER_HEALTH_SCORE_LABEL = ["cluster", "type"]
CLUSTER_HEALTH_METRIC_LABEL = ["cluster", "type", "description", "mode"]
NODE_HEALTH_SCORE_LABEL = ["cluster", "instance", "type"]
NODE_HEALTH_METRIC_LABEL = [
    "cluster",
    "instance",
    "type",
    "description",
    "mode"]
POD_HEALTH_SCORE_LABEL = ["cluster", "instance", "pod", "namespace", "type"]
POD_HEALTH_METRIC_LABEL = [
    "cluster",
    "instance",
    "type",
    "pod",
    "namespace",
    "description",
    "mode"]

registry = CollectorRegistry()
cluster_health_score = Gauge('sysom_cluster_health_score',
                             'sysom cluster health score',
                             CLUSTER_HEALTH_SCORE_LABEL,
                             registry=registry)
cluster_health_metric = Gauge('sysom_cluster_health_metric',
                              'sysom cluster health metric',
                              CLUSTER_HEALTH_METRIC_LABEL,
                              registry=registry)
node_health_score = Gauge('sysom_node_health_score',
                          'sysom node health score',
                          NODE_HEALTH_SCORE_LABEL,
                          registry=registry)
node_health_metric = Gauge('sysom_node_health_metric',
                           'sysom node health metric',
                           NODE_HEALTH_METRIC_LABEL,
                           registry=registry)
pod_health_score = Gauge('sysom_pod_health_score',
                         'sysom pod health score',
                         POD_HEALTH_SCORE_LABEL,
                         registry=registry)
pod_health_metric = Gauge('sysom_pod_health_metric',
                          'sysom pod health score',
                          POD_HEALTH_METRIC_LABEL,
                          registry=registry)

g_cache_cluster = SysomFramework.gcache("cluster_metrics")
g_cache_instance = SysomFramework.gcache("instance_metrics")
g_cache_pod = SysomFramework.gcache("pod_metrics")

#############################################################################
# Write your API interface here, or add to app/routes
#############################################################################


def init_framwork():
    SysomFramework\
        .init(YAML_CONFIG) \
        .load_plugin_cls(CmgPlugin) \
        .start()
    logger.info("SysomFramework init finished!")


@app.on_event("startup")
async def on_start():
    init_framwork()

    # cleanup history data befor startup
    g_cache_cluster.clean()
    g_cache_instance.clean()
    g_cache_pod.clean()

    # load all registered metrics from settings
    metric_manager = MetricManager()
    metric_manager.metric_register()
    weight_calculator = WeightsCalculator(metric_manager)

    diagnose_queue = Queue(maxsize=settings.MAX_QUEUE_SIZE)
    pid = getpid();

    # start analyzer to collect and calculate health score
    Analyzer(clusterhealth_interval=settings.CALCULATE_INTERVAL,
             queue=diagnose_queue,
             metric_manager=metric_manager,
             weight_cal=weight_calculator,
             parent_pid=pid).start()

    DiagnoseWorker(
        metric_manager=metric_manager,
        queue=diagnose_queue,
        parent_pid=pid).start()

    logger.info("集群健康度计算定时任务已启动")

    ##########################################################################
    # Perform some microservice initialization operations over here
    ##########################################################################


@app.on_event("shutdown")
async def on_shutdown():
    pass


@app.get("/metrics")
def get_metrics():
    # pull health score metric from redis and push to prometheus
    try:
        cluster_all = g_cache_cluster.load_all()
        nodes_all = g_cache_instance.load_all()
        pods_all = g_cache_pod.load_all()

        if len(cluster_all) <= 0 or len(nodes_all) <= 0:
            return Response(generate_latest(registry), media_type="text/plain")

        def process_metrics(metrics_all, score_labels, metric_labels,
                            health_score, health_metric, cache):
            for item, results in metrics_all.items():
                metrics = json.loads(results)

                # the last element is the health score
                for metric in metrics:
                    if metric["type"] == ScoreType.MetricScore.value:
                        labels = [metric["labels"][label]
                                  for label in metric_labels[:-1]] + ["score"]
                        # return score of each metric
                        health_metric.labels(*labels).set(metric["score"])
                        # return value of each metric
                        labels[-1] = "value"
                        health_metric.labels(*labels).set(metric["value"])
                    elif metric["type"] == ScoreType.MetricTypeScore.value:
                        labels = [metric["labels"][label]
                                  for label in score_labels]
                        # return score of each metric
                        health_score.labels(*labels).set(metric["score"])
                    elif metric["type"] == ScoreType.InstanceScore.value:
                        labels = [metric["labels"][label]
                                  for label in score_labels[:-1]] + ["total"]
                        # return score of each metric
                        health_score.labels(*labels).set(metric["score"])

                # delete metrics from redis
                cache.delete(item)

        # Then call the function with the appropriate arguments
        process_metrics(cluster_all, CLUSTER_HEALTH_SCORE_LABEL,
                        CLUSTER_HEALTH_METRIC_LABEL,
                        cluster_health_score,
                        cluster_health_metric, g_cache_cluster)
        process_metrics(nodes_all, NODE_HEALTH_SCORE_LABEL,
                        NODE_HEALTH_METRIC_LABEL,
                        node_health_score,
                        node_health_metric, g_cache_instance)
        process_metrics(pods_all, POD_HEALTH_SCORE_LABEL,
                        POD_HEALTH_METRIC_LABEL,
                        pod_health_score,
                        pod_health_metric, g_cache_pod)

    except Exception as e:
        print("Exception: ", e)
        raise HTTPException(status_code=400, detail=str(e))
    finally:
        g_cache_cluster.clean()
        g_cache_instance.clean()
        g_cache_pod.clean()

    return Response(generate_latest(registry), media_type="text/plain")
